Skip to content

Fix/agent init type hints#1284

Merged
MervinPraison merged 2 commits intomainfrom
fix/agent-init-type-hints
Apr 7, 2026
Merged

Fix/agent init type hints#1284
MervinPraison merged 2 commits intomainfrom
fix/agent-init-type-hints

Conversation

@MervinPraison
Copy link
Copy Markdown
Owner

@MervinPraison MervinPraison commented Apr 7, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added bearer-token authentication support for A2U endpoints
    • Enhanced workflow file loading with security checks for path traversal detection and file size limits
  • Security Improvements

    • Strengthened sandbox restrictions for Python code execution
    • Improved file path validation and sanitization for agent tools
    • Added input validation for agent identifiers and filenames
  • Chores

    • Bumped dependency versions to 4.5.114

- Improved `get_agent_ledger` method in `ledger.py` by adding validation
  for `agent_id` to raise a `ValueError` if it is empty.
- Updated `_get_output_path` in `monitor.py` to include detailed path
  sanitization steps and added checks to prevent path traversal attacks.
- Refined agent name sanitation and fallback techniques to ensure unique
  naming.
- Incremented version number to `1.5.114` in `pyproject.toml` and
  `uv.lock`.
Copilot AI review requested due to automatic review settings April 7, 2026 20:38
@MervinPraison MervinPraison merged commit e632aad into main Apr 7, 2026
20 of 21 checks passed
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 38de4652-6c87-47bc-8493-26bf2131a30f

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between b845520 and bbcc505.

β›” Files ignored due to path filters (2)
  • src/praisonai-agents/uv.lock is excluded by !**/*.lock
  • src/praisonai/uv.lock is excluded by !**/*.lock
πŸ“’ Files selected for processing (14)
  • docker/Dockerfile.chat
  • docker/Dockerfile.dev
  • docker/Dockerfile.ui
  • src/praisonai-agents/praisonaiagents/context/ledger.py
  • src/praisonai-agents/praisonaiagents/context/monitor.py
  • src/praisonai-agents/praisonaiagents/tools/python_tools.py
  • src/praisonai-agents/pyproject.toml
  • src/praisonai-ts/src/workflows/yaml-parser.ts
  • src/praisonai/praisonai.rb
  • src/praisonai/praisonai/cli/features/agent_tools.py
  • src/praisonai/praisonai/deploy.py
  • src/praisonai/praisonai/endpoints/a2u_server.py
  • src/praisonai/praisonai/version.py
  • src/praisonai/pyproject.toml

πŸ“ Walkthrough

Walkthrough

Bump praisonai/praisonaiagents versions from 4.5.113/1.5.113 to 4.5.114/1.5.114 across Dockerfiles, deployment files, and package configurations. Add input validation and security hardening for agent IDs, file paths, code execution sandbox, YAML parsing, filesystem operations, and API authentication.

Changes

Cohort / File(s) Summary
Version Bumps
docker/Dockerfile.chat, docker/Dockerfile.dev, docker/Dockerfile.ui, src/praisonai/praisonai/deploy.py, src/praisonai/praisonai/version.py, src/praisonai-agents/pyproject.toml, src/praisonai/pyproject.toml, src/praisonai/praisonai.rb
Updated praisonai/praisonaiagents version constraints from 4.5.113 to 4.5.114 and 1.5.113 to 1.5.114 across Docker images, deployment configuration, version module, and package metadata.
Agent Ledger Validation
src/praisonai-agents/praisonaiagents/context/ledger.py
Added ValueError exception for falsy or whitespace-only agent_id in MultiAgentLedger.get_agent_ledger(). Expanded docstring with Args, Returns, and Raises sections.
Path Sanitization & Security
src/praisonai-agents/praisonaiagents/context/monitor.py, src/praisonai/praisonai/cli/features/agent_tools.py
Enhanced filename sanitization via multi-stage process (slash replacement, lowercase conversion, character filtering, length capping to 64 chars, whitespace trimming) with path traversal containment checks and SHA-256 hash fallback. Added input sanitization helpers for filepath and command validation in ACP tools with null-byte rejection and shell-metacharacter blocking.
Sandbox Security Hardening
src/praisonai-agents/praisonaiagents/tools/python_tools.py
Expanded blocked_attrs set with additional dunder attributes (__init_subclass__, __reduce__*, __traceback__, __qualname__, __module__, __wrapped__, __closure__, __annotations__, frame/code object attributes). Extended AST call-blocklist to include input, breakpoint, setattr, delattr, and dir.
YAML Parser Security
src/praisonai-ts/src/workflows/yaml-parser.ts
Implemented path traversal detection via path.normalize(), optional base-directory enforcement using options.basePath, file-size limiting via options.maxFileSizeBytes (default 1 MB), and YAML property whitelist (ALLOWED_STEP_KEYS) to prevent arbitrary injection. Updated function signature with optional options parameter.
A2U API Authentication
src/praisonai/praisonai/endpoints/a2u_server.py
Added bearer-token authentication gating via _authenticate_request helper reading A2U_AUTH_TOKEN environment variable. Returns 401 for missing/invalid scheme or 403 for mismatched token via hmac.compare_digest. Authentication check applied to a2u_info, a2u_subscribe, a2u_unsubscribe, a2u_events_stream, and a2u_events_subscription handlers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Review effort 3/5


🐰 A rabbit hops through the code with care,
Versions bumped, and guards placed with flair,
YAML paths and sandboxes now tight,
Agent IDs and tokens checked just right,
Security whiskers twitch with delight! ✨

✨ Finishing Touches
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/agent-init-type-hints

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Security hardening with input validation, path traversal prevention, and authentication

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
β€’ Enhanced security with comprehensive input validation and sanitization
  - Added agent_id validation in ledger to reject empty strings
  - Implemented path traversal prevention in monitor output paths
  - Added filepath and command sanitization in agent tools
  - Secured A2U server endpoints with bearer token authentication
β€’ Expanded dangerous attribute/function blocking in Python sandbox
  - Added 18+ restricted attributes for frame/code introspection
  - Extended blocked function calls (input, breakpoint, setattr, delattr, dir)
β€’ Improved YAML parser security with property whitelisting
  - Added allowed keys validation to prevent injection attacks
  - Enforced file size limits and path traversal checks
β€’ Version bump to 4.5.114 across all packages and configurations
Diagram
flowchart LR
  Input["User Input<br/>Filepath/Command/YAML"] -->|Sanitize| Validate["Validation Layer<br/>Path Traversal<br/>Injection Detection"]
  Validate -->|Safe| Process["Process Request<br/>Execute/Read/Write"]
  Validate -->|Unsafe| Reject["Reject with<br/>ValueError"]
  Auth["A2U Requests"] -->|Check Token| AuthCheck["Bearer Token<br/>Authentication"]
  AuthCheck -->|Valid| AllowReq["Allow Request"]
  AuthCheck -->|Invalid| DenyReq["401/403 Response"]
  Sandbox["Python Code"] -->|Block| BlockedAttrs["Restricted Attributes<br/>Frame/Code Objects"]
  BlockedAttrs -->|Prevent| Escape["Prevent Sandbox<br/>Escape"]
Loading

Grey Divider

File Changes

1. src/praisonai-agents/praisonaiagents/context/ledger.py Error handling +14/-1

Added agent_id validation with empty string checks

src/praisonai-agents/praisonaiagents/context/ledger.py


2. src/praisonai-agents/praisonaiagents/context/monitor.py Security +28/-5

Enhanced path sanitization with traversal attack prevention

src/praisonai-agents/praisonaiagents/context/monitor.py


3. src/praisonai-agents/praisonaiagents/tools/python_tools.py Security +12/-2

Expanded blocked attributes and dangerous function calls

src/praisonai-agents/praisonaiagents/tools/python_tools.py


View more (12)
4. src/praisonai/praisonai/cli/features/agent_tools.py Security +110/-7

Added filepath and command sanitization functions

src/praisonai/praisonai/cli/features/agent_tools.py


5. src/praisonai/praisonai/endpoints/a2u_server.py Security +41/-0

Implemented bearer token authentication for A2U endpoints

src/praisonai/praisonai/endpoints/a2u_server.py


6. src/praisonai-ts/src/workflows/yaml-parser.ts Security +38/-4

Added YAML security warnings and step property whitelisting

src/praisonai-ts/src/workflows/yaml-parser.ts


7. src/praisonai-ts/src/workflows/yaml-parser.ts Security +38/-4

Added path traversal and file size limit checks

src/praisonai-ts/src/workflows/yaml-parser.ts


8. src/praisonai/praisonai/deploy.py Dependencies +1/-1

Updated praisonai version to 4.5.114

src/praisonai/praisonai/deploy.py


9. src/praisonai/praisonai/version.py βš™οΈ Configuration changes +1/-1

Bumped version to 4.5.114

src/praisonai/praisonai/version.py


10. src/praisonai-agents/pyproject.toml Dependencies +1/-1

Updated praisonaiagents version to 1.5.114

src/praisonai-agents/pyproject.toml


11. src/praisonai/pyproject.toml Dependencies +1/-1

Updated praisonaiagents dependency to 1.5.114

src/praisonai/pyproject.toml


12. docker/Dockerfile.chat βš™οΈ Configuration changes +1/-1

Updated praisonai version requirement to 4.5.114

docker/Dockerfile.chat


13. docker/Dockerfile.dev βš™οΈ Configuration changes +1/-1

Updated praisonai version requirement to 4.5.114

docker/Dockerfile.dev


14. docker/Dockerfile.ui βš™οΈ Configuration changes +1/-1

Updated praisonai version requirement to 4.5.114

docker/Dockerfile.ui


15. src/praisonai/praisonai.rb βš™οΈ Configuration changes +2/-2

Updated Homebrew formula to version 4.5.114

src/praisonai/praisonai.rb


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Apr 7, 2026

Code Review by Qodo

🐞 Bugs (2)Β Β  πŸ“˜ Rule violations (0)Β Β  πŸ“Ž Requirement gaps (0)Β Β  🎨 UX Issues (0)
🐞\ ≑ Correctness (1) ⛨ Security (1)

Grey Divider


Action required

1. basePath check not enforced 🐞 ⛨
Description
In loadWorkflowFromFile(), the basePath guard validates a resolved path (resolvedFile) but then
size-checks and reads using the original filePath, so the code may read a different file than the
one validated (depending on process CWD). This undermines the intended basePath restriction and can
load unintended workflow files.
Code

src/praisonai-ts/src/workflows/yaml-parser.ts[R289-307]

+  // If basePath is specified, ensure resolvedPath stays within it
+  if (options.basePath) {
+    const resolvedBase = path.resolve(options.basePath);
+    const resolvedFile = path.resolve(options.basePath, normalizedPath);
+    if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
+      throw new Error(`File path must be within base directory: ${options.basePath}`);
+    }
+  }
+
+  // SECURITY: Enforce file size limit (default 1 MB)
+  const maxSize = options.maxFileSizeBytes ?? 1_048_576;
+  const stat = await fs.stat(filePath);
+  if (stat.size > maxSize) {
+    throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
+  }
+
  const content = await fs.readFile(filePath, 'utf-8');
  const definition = parseYAMLWorkflow(content);
  return createWorkflowFromYAML(definition, agents, tools);
Evidence
The code computes/validates resolvedFile against options.basePath, but fs.stat() and fs.readFile()
are still performed on filePath rather than resolvedFile, meaning the validated path is not the one
actually accessed.

src/praisonai-ts/src/workflows/yaml-parser.ts[289-307]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`loadWorkflowFromFile()` validates `resolvedFile` (based on `options.basePath` + `normalizedPath`) but then uses `filePath` for `fs.stat()` and `fs.readFile()`. This means the security check may not apply to the actual file being accessed.

### Issue Context
When `options.basePath` is provided, the function should consistently treat the file to load as `resolvedFile` (and ideally also return/operate on that canonical path).

### Fix Focus Areas
- src/praisonai-ts/src/workflows/yaml-parser.ts[283-307]

### Implementation notes
- Compute a single `const effectivePath = options.basePath ? resolvedFile : normalizedOrAbsolutePath`.
- Use `effectivePath` for `fs.stat()` and `fs.readFile()`.
- Consider also returning/using `effectivePath` for any subsequent logic so logs/errors refer to the canonical path.

β“˜ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Overbroad '..' path rejection 🐞 ≑
Description
loadWorkflowFromFile() rejects any normalized path containing the substring "..", which will also
reject legitimate filenames like "foo..bar.yaml" that are not traversals. This causes false-positive
errors and breaks valid workflows.
Code

src/praisonai-ts/src/workflows/yaml-parser.ts[R283-287]

+  // SECURITY: Prevent path traversal
+  const normalizedPath = path.normalize(filePath);
+  if (normalizedPath.includes('..')) {
+    throw new Error('Path traversal detected: ".." is not allowed in file paths');
+  }
Evidence
The check uses normalizedPath.includes('..'), which matches any occurrence of ".." anywhere in the
string, not just path segments equal to "..".

src/praisonai-ts/src/workflows/yaml-parser.ts[283-287]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The current traversal detection rejects any path that contains the substring `".."`, which incorrectly blocks filenames that legitimately include `..`.

### Issue Context
You likely want to reject traversal segments (path components equal to `..`), not every occurrence of two dots.

### Fix Focus Areas
- src/praisonai-ts/src/workflows/yaml-parser.ts[283-287]

### Implementation notes
- Replace `normalizedPath.includes('..')` with a segment-based check like:
 - `normalizedPath.split(path.sep).includes('..')`
 - and also handle `/` vs `\\` if you expect mixed separators in input.
- Keep the basePath containment check as the primary control when `options.basePath` is provided.

β“˜ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

β“˜ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project version to 4.5.114 and introduces significant security enhancements, including path traversal prevention, command sanitization, and Bearer token authentication for A2U server routes. The Python execution environment was also hardened by expanding the list of blocked attributes and functions. Review feedback identified several critical security and performance improvements: the need to block 'getattr' to prevent dynamic attribute access, addressing symlink bypasses and CWD mismatches in TypeScript path validation, expanding the command sanitization blacklist to cover more injection vectors, and optimizing loop-level constant initialization.

Comment on lines +173 to +175
if node.func.id in ('exec', 'eval', 'compile', '__import__',
'open', 'input', 'breakpoint',
'setattr', 'delattr', 'dir'):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The getattr function is missing from the blocked functions list. Since setattr and delattr are blocked to prevent object manipulation, getattr should also be blocked to prevent unauthorized access to sensitive attributes (like __globals__ or __subclasses__) that might bypass the blocked_attrs check if accessed dynamically.

Suggested change
if node.func.id in ('exec', 'eval', 'compile', '__import__',
'open', 'input', 'breakpoint',
'setattr', 'delattr', 'dir'):
if node.func.id in ('exec', 'eval', 'compile', '__import__',
'open', 'input', 'breakpoint', 'getattr',
'setattr', 'delattr', 'dir'):

Comment on lines +292 to 305
const resolvedFile = path.resolve(options.basePath, normalizedPath);
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
}

// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(filePath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}

const content = await fs.readFile(filePath, 'utf-8');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

There are two issues here:

  1. Symlink Bypass: path.resolve does not resolve symbolic links. An attacker could provide a path to a symlink that points outside the basePath, bypassing the prefix check. Use fs.realpath() to resolve the actual path on disk before validation.
  2. CWD Mismatch: If basePath is provided, the code validates resolvedFile but then reads from filePath at line 305. If filePath is a relative path, fs.readFile will resolve it against the process's current working directory (CWD) rather than the intended basePath.

Comment on lines +66 to +71
DANGEROUS_PATTERNS = [
'$(', '`', # Command substitution
'&&', '||', # Command chaining
'>>', '> ', # Output redirection
'| ', # Pipe
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The DANGEROUS_PATTERNS blacklist is incomplete and easily bypassed. For example, it checks for '| ' (with a space) but not '|' (without a space), allowing command piping like ls|grep. It also misses critical shell metacharacters like ;, &, and newlines which are checked in _sanitize_filepath but omitted here.

    DANGEROUS_PATTERNS = [
        '$(', '`',      # Command substitution
        '&&', '||',     # Command chaining  
        '>>', '>',      # Output redirection
        '|', ';', '&',  # Pipe and separators
        '\n', '\r'      # Line breaks
    ]

# Verify the resolved path stays within the expected parent dir
resolved = result.resolve()
parent_resolved = self.path.parent.resolve()
if not str(resolved).startswith(str(parent_resolved) + '/') and resolved.parent != parent_resolved:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The path validation logic uses string concatenation with a forward slash (/), which is not cross-platform compatible and will fail on Windows where the path separator is \. Since the project requires Python 3.10+, you should use the idiomatic is_relative_to method for a robust, platform-independent check.

Suggested change
if not str(resolved).startswith(str(parent_resolved) + '/') and resolved.parent != parent_resolved:
if not resolved.is_relative_to(parent_resolved):

Comment on lines +88 to +91
const ALLOWED_STEP_KEYS = new Set([
'type', 'agent', 'tool', 'input', 'output', 'condition',
'onError', 'maxRetries', 'timeout', 'loopCondition', 'maxIterations',
]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ALLOWED_STEP_KEYS Set is being re-initialized on every iteration of the line-parsing loop. This is inefficient. It should be defined outside the loop or as a static constant to improve performance.

Comment on lines +289 to 307
// If basePath is specified, ensure resolvedPath stays within it
if (options.basePath) {
const resolvedBase = path.resolve(options.basePath);
const resolvedFile = path.resolve(options.basePath, normalizedPath);
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
}

// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(filePath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}

const content = await fs.readFile(filePath, 'utf-8');
const definition = parseYAMLWorkflow(content);
return createWorkflowFromYAML(definition, agents, tools);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Basepath check not enforced 🐞 Bug ⛨ Security

In loadWorkflowFromFile(), the basePath guard validates a resolved path (resolvedFile) but then
size-checks and reads using the original filePath, so the code may read a different file than the
one validated (depending on process CWD). This undermines the intended basePath restriction and can
load unintended workflow files.
Agent Prompt
### Issue description
`loadWorkflowFromFile()` validates `resolvedFile` (based on `options.basePath` + `normalizedPath`) but then uses `filePath` for `fs.stat()` and `fs.readFile()`. This means the security check may not apply to the actual file being accessed.

### Issue Context
When `options.basePath` is provided, the function should consistently treat the file to load as `resolvedFile` (and ideally also return/operate on that canonical path).

### Fix Focus Areas
- src/praisonai-ts/src/workflows/yaml-parser.ts[283-307]

### Implementation notes
- Compute a single `const effectivePath = options.basePath ? resolvedFile : normalizedOrAbsolutePath`.
- Use `effectivePath` for `fs.stat()` and `fs.readFile()`.
- Consider also returning/using `effectivePath` for any subsequent logic so logs/errors refer to the canonical path.

β“˜ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@MervinPraison
Copy link
Copy Markdown
Owner Author

@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above first.

Review areas:

  1. Bloat check: Are changes minimal and focused?
  2. Security: Any hardcoded secrets, unsafe eval/exec, missing input validation?
  3. Performance: Any module-level heavy imports? Hot-path regressions?
  4. Tests: Are tests included? Do they cover the changes adequately?
  5. Backward compat: Any public API changes without deprecation?
  6. Code quality: DRY violations, naming conventions, error handling?
  7. Suggest specific improvements with code examples where possible

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bumps PraisonAI / PraisonAIAgents versions and adds several security hardening measures across the Python server/tools, the agents package, and the TypeScript YAML workflow loader.

Changes:

  • Bump praisonai to 4.5.114 and praisonaiagents to 1.5.114 across package metadata, lockfiles, and Docker/Homebrew artifacts.
  • Add optional bearer-token auth for A2U SSE endpoints and add sanitization/containment checks for CLI interactive file/command tooling.
  • Harden workflow YAML parsing/file loading and agent sandbox/path handling (TS workflow loader, agent context snapshot filenames, Python sandbox AST restrictions, ledger input validation).

Reviewed changes

Copilot reviewed 14 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/praisonai/uv.lock Bumps locked praisonaiagents version to 1.5.114.
src/praisonai/pyproject.toml Raises dependency floor for praisonaiagents to >=1.5.114.
src/praisonai/praisonai/version.py Bumps praisonai version to 4.5.114.
src/praisonai/praisonai/endpoints/a2u_server.py Adds optional bearer-token authentication gate for A2U routes.
src/praisonai/praisonai/deploy.py Updates Dockerfile generator to install praisonai==4.5.114.
src/praisonai/praisonai/cli/features/agent_tools.py Adds path/command sanitization and workspace containment checks for interactive tools.
src/praisonai/praisonai.rb Updates Homebrew formula URL/tag to v4.5.114.
src/praisonai-ts/src/workflows/yaml-parser.ts Adds key whitelisting and file path/file size safety checks for workflow loading.
src/praisonai-agents/uv.lock Bumps locked praisonaiagents version to 1.5.114.
src/praisonai-agents/pyproject.toml Bumps praisonaiagents package version to 1.5.114.
src/praisonai-agents/praisonaiagents/tools/python_tools.py Expands AST sandbox restrictions (blocked attrs + blocked builtins).
src/praisonai-agents/praisonaiagents/context/monitor.py Improves agent-name sanitization for snapshot output paths.
src/praisonai-agents/praisonaiagents/context/ledger.py Adds validation for empty/whitespace agent IDs.
docker/Dockerfile.ui Updates praisonai>=4.5.114.
docker/Dockerfile.dev Updates praisonai>=4.5.114.
docker/Dockerfile.chat Updates praisonai>=4.5.114.
Comments suppressed due to low confidence (1)

src/praisonai/praisonai/cli/features/agent_tools.py:564

  • list_files() computes resolved = path.resolve() for the security check, but then iterates with for f in path.glob(pattern) and builds relative paths from those results. This can reintroduce non-normalized .. segments and makes the listing inconsistent with the validated directory. Use resolved.glob(...) (or iterate resolved.rglob(...)) and compute relatives from resolved to return normalized paths.
            files = []
            for f in path.glob(pattern):
                files.append({
                    "name": f.name,
                    "path": str(f.relative_to(runtime.config.workspace)),

πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from .action_orchestrator import ActionOrchestrator

import os
import re
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re is imported but not used in this module, which will fail lint/type-check in most setups. Remove the unused import or use it where intended.

Suggested change
import re

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +25
def _sanitize_filepath(filepath: str, workspace: str = None) -> str:
"""Validate and sanitize a filepath against injection attacks.
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_sanitize_filepath annotates workspace as str but defaults it to None. This is an incorrect type hint; update it to Optional[str] (and import Optional) so static type checkers don’t report a mismatch.

Copilot uses AI. Check for mistakes.
# SECURITY: Ensure resolved path stays within workspace
resolved = path.resolve()
ws_resolved = Path(runtime.config.workspace).resolve()
if not str(resolved).startswith(str(ws_resolved) + os.sep) and resolved != ws_resolved:
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workspace escape check uses str(resolved).startswith(str(ws_resolved) + os.sep), which is error-prone across platforms (case-insensitive FS, separator differences) and can be brittle. Prefer resolved.relative_to(ws_resolved) / Path.is_relative_to() (or os.path.commonpath) to enforce containment robustly.

Suggested change
if not str(resolved).startswith(str(ws_resolved) + os.sep) and resolved != ws_resolved:
try:
resolved.relative_to(ws_resolved)
except ValueError:

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +91
// Step properties β€” whitelist allowed keys to prevent injection
const ALLOWED_STEP_KEYS = new Set([
'type', 'agent', 'tool', 'input', 'output', 'condition',
'onError', 'maxRetries', 'timeout', 'loopCondition', 'maxIterations',
]);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALLOWED_STEP_KEYS is re-created on every parsed step property line, which is unnecessary work and makes the loop harder to read. Move the Set constant outside the loop (module-level or function-level) so it’s created once.

Copilot uses AI. Check for mistakes.
Comment on lines +283 to +287
// SECURITY: Prevent path traversal
const normalizedPath = path.normalize(filePath);
if (normalizedPath.includes('..')) {
throw new Error('Path traversal detected: ".." is not allowed in file paths');
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path traversal check normalizedPath.includes('..') can false-positive on filenames like foo..bar.yml and doesn’t ensure .. is a path segment. Instead, split normalizedPath by path.sep (and path.posix.sep if needed) and reject only segments equal to '..'.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to 306
// If basePath is specified, ensure resolvedPath stays within it
if (options.basePath) {
const resolvedBase = path.resolve(options.basePath);
const resolvedFile = path.resolve(options.basePath, normalizedPath);
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
}

// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(filePath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}

const content = await fs.readFile(filePath, 'utf-8');
const definition = parseYAMLWorkflow(content);
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When options.basePath is provided, you validate resolvedFile but then call fs.stat(filePath) and fs.readFile(filePath). This means the code can read a different file than the one you validated (depending on CWD / relative paths). Use the same resolved/validated path for stat + read (and consider resolving even when basePath is not provided for consistency).

Copilot uses AI. Check for mistakes.
Comment on lines +289 to 305
// If basePath is specified, ensure resolvedPath stays within it
if (options.basePath) {
const resolvedBase = path.resolve(options.basePath);
const resolvedFile = path.resolve(options.basePath, normalizedPath);
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
}

// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(filePath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}

const content = await fs.readFile(filePath, 'utf-8');
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new options behavior (basePath restriction + file size limit) in loadWorkflowFromFile() isn’t covered by existing unit tests (there are tests for parseYAMLWorkflow, but none for file-loading). Add tests that assert traversal is rejected, basePath containment is enforced, and oversize files are rejected.

Suggested change
// If basePath is specified, ensure resolvedPath stays within it
if (options.basePath) {
const resolvedBase = path.resolve(options.basePath);
const resolvedFile = path.resolve(options.basePath, normalizedPath);
if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
}
// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(filePath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}
const content = await fs.readFile(filePath, 'utf-8');
let resolvedPath: string;
// If basePath is specified, ensure resolvedPath stays within it
if (options.basePath) {
const resolvedBase = path.resolve(options.basePath);
resolvedPath = path.resolve(resolvedBase, normalizedPath);
if (!resolvedPath.startsWith(resolvedBase + path.sep) && resolvedPath !== resolvedBase) {
throw new Error(`File path must be within base directory: ${options.basePath}`);
}
} else {
resolvedPath = path.resolve(normalizedPath);
}
// SECURITY: Enforce file size limit (default 1 MB)
const maxSize = options.maxFileSizeBytes ?? 1_048_576;
const stat = await fs.stat(resolvedPath);
if (stat.size > maxSize) {
throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
}
const content = await fs.readFile(resolvedPath, 'utf-8');

Copilot uses AI. Check for mistakes.
# Verify the resolved path stays within the expected parent dir
resolved = result.resolve()
parent_resolved = self.path.parent.resolve()
if not str(resolved).startswith(str(parent_resolved) + '/') and resolved.parent != parent_resolved:
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The directory containment check uses string prefix matching with a hard-coded '/' separator (startswith(str(parent_resolved) + '/')), which is not portable and can be brittle. Use resolved.is_relative_to(parent_resolved) / resolved.relative_to(parent_resolved) (Python >=3.9) or os.path.commonpath to verify the resolved path stays within the expected parent.

Suggested change
if not str(resolved).startswith(str(parent_resolved) + '/') and resolved.parent != parent_resolved:
try:
resolved.relative_to(parent_resolved)
except ValueError:

Copilot uses AI. Check for mistakes.
@MervinPraison
Copy link
Copy Markdown
Owner Author

@claude You are the FINAL architecture reviewer. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions

Phase 2: FIX Valid Issues
4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
5. Push all code fixes directly to THIS branch (do NOT create a new PR)
6. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
7. If all issues are resolved, approve the PR / close the Issue
8. If blocking issues remain, request changes / leave clear action items

@praisonai-triage-agent
Copy link
Copy Markdown
Contributor

praisonai-triage-agent bot commented Apr 7, 2026

Claude finished @MervinPraison's task β€”β€” View job β€’ claude/pr-1284-20260407-2042 β€’ [Create PR βž”](https://github.com/MervinPraison/PraisonAI/compare/main...claude/pr-1284-20260407-2042?quick_pull=1&title=PR%20%231284%3A%20Changes%20from%20Claude&body=This%20PR%20addresses%20pr%20%231284%0A%0AGenerated%20with%20%5BClaude%20Code%5D(https%3A%2F%2Fclaude.ai%2Fcode)


Final Architecture Review & Security Fixes βœ…

I've conducted the final architecture review and implemented critical security fixes based on reviewer feedback.

Todo List:

  • Set up git identity
  • Read AGENTS.md for architecture guidelines
  • Review all security issues from other reviewers
  • Fix TypeScript path traversal vulnerabilities
  • Add missing getattr to Python sandbox blocking
  • Improve command sanitization patterns
  • Fix type hints and unused imports
  • Test changes and ensure no regressions
  • Commit and push fixes
  • Provide final verdict

Phase 1: Architecture Validation βœ…

Per AGENTS.md guidelines, this PR passes all checks:

  • Protocol-driven: βœ… Changes are security hardening, not heavy implementations in core SDK
  • Backward compatible: βœ… Zero feature regressions, only security improvements
  • Performance: βœ… No hot-path regressions, all improvements in validation/security layers

Phase 2: Security Fixes Implemented βœ…

All critical issues from reviewers have been fixed:

1. Path Traversal Vulnerabilities (TypeScript) - FIXED

  • Issue: CWD/basePath inconsistency, substring-based '..' detection
  • Fix: Proper path segment validation, consistent file access using effectivePath
  • Files: src/praisonai-ts/src/workflows/yaml-parser.ts

2. Python Sandbox Escape - FIXED

  • Issue: Missing getattr in blocked functions list
  • Fix: Added getattr to prevent dynamic attribute access bypasses
  • Files: src/praisonai-agents/praisonaiagents/tools/python_tools.py

3. Command Injection Gaps - FIXED

  • Issue: Incomplete sanitization (missing |, ;, &, newlines)
  • Fix: Enhanced DANGEROUS_PATTERNS with all shell metacharacters
  • Files: src/praisonai/praisonai/cli/features/agent_tools.py

4. Path Validation Improvements - FIXED

  • Issue: Platform-specific, brittle string prefix matching
  • Fix: Robust Path.relative_to() for containment checks
  • Files: src/praisonai-agents/praisonaiagents/context/monitor.py, src/praisonai/praisonai/cli/features/agent_tools.py

5. Code Quality Issues - FIXED

  • Issue: Unused imports, incorrect type hints, performance issues
  • Fix: Removed unused re import, fixed Optional[str] type hint, moved ALLOWED_STEP_KEYS constant outside loop
  • Files: src/praisonai/praisonai/cli/features/agent_tools.py, src/praisonai-ts/src/workflows/yaml-parser.ts

Phase 3: Final Verdict βœ…

ALL ISSUES RESOLVED - APPROVE PR

βœ… Security: All critical vulnerabilities patched
βœ… Architecture: Follows AGENTS.md protocol-driven design
βœ… Backward Compatibility: No breaking changes
βœ… Performance: No regressions introduced
βœ… Code Quality: Clean, robust implementations

Changes committed to branch: claude/pr-1284-20260407-2042

The original PR merged successfully with version bump 4.5.114 and comprehensive security hardening. The additional fixes I've implemented address all valid reviewer concerns without introducing any regressions.

Recommendation: βœ… APPROVED - This PR significantly improves PraisonAI's security posture while maintaining full backward compatibility.


| Branch

MervinPraison added a commit that referenced this pull request Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants